﻿using System;
using System.Collections.Generic;
using System.Linq;
using Eneta.Portal.Common.Dto;
using Microsoft.SharePoint;

namespace Eneta.Portal.Common.Repository
{
    public class EventRepository : BaseRepository 
    {
        public EventRepository(SPWeb web) : base(web)
        {
            
        }

        public IList<string> GetAllTargetAudiences()
        {
            IList<string> audiences = new List<string>();

            var sec = new SPSecurity.CodeToRunElevated(delegate
                       {
                           using (var site = new SPSite(SiteId))
                           using (var web = site.OpenWeb("/sundmused/"))
                           {
                               var list = web.Lists["Lehed"];
                               var field = (SPFieldMultiChoice) list.Fields["TargetAudience"];
                               foreach (var choice in field.Choices)
                                   audiences.Add(choice);
                           }
                       });
            SPSecurity.RunWithElevatedPrivileges(sec);

            return audiences;
        }

        public bool IsRegistered(SPUser user, int eventId)
        {
            if (user == null)
                return false;
            if (eventId <= 0)
                return false;

            var isRegistered = false;

            var sec = new SPSecurity.CodeToRunElevated(delegate 
            {
                using(var site = new SPSite(SiteId))
                using(var web = site.OpenWeb("/sundmused/"))
                {
                    var item = GetAttendeeItem(web, user.Name, eventId);
                    isRegistered = (item != null);
                }
            });
            SPSecurity.RunWithElevatedPrivileges(sec);

            return isRegistered;
        }

        private SPListItem GetAttendeeItem(SPWeb web, string userDisplayName, int eventId)
        {
            var list = web.Lists["Registrations"];
            Logger.LogInfo("Enne registreerijaid", "EventRepository_GetAttendeeItem");
            var queryString = "<Where><And>";
            queryString += "<Eq><FieldRef Name='Attendee' /><Value Type='User'>{0}</Value></Eq>";
            queryString += "<Eq><FieldRef Name='Event' LookupId='True' /><Value Type='Lookup'>{1}</Value></Eq>";
            queryString += "</And></Where>";

            queryString = string.Format(queryString, userDisplayName, eventId);
            var items = ExecuteQuery(list, queryString);
            if (items == null)
                return null;
            if (items.Count == 0)
                return null;
            Logger.LogInfo("Pärast registreerijaid", "EventRepository_GetAttendeeItem");
            return items[0];
        }

        public bool RegiterUserToEvent(SPUser user, int eventId, bool toVirtualEvent)
        {
            // Invalid data, do nothing
            if (user == null)
                return false;
            if (eventId <= 0)
                return false;

            // User is already registered
            if (IsRegistered(user, eventId))
                return true;

            // Get event and return if it does not exist
            var evt = GetEventById(eventId);
            if (evt == null)
                return false;

            // Registrations are allowed only to given time date
            if (evt.RegistrationEndDate.Date < DateTime.Now.Date)
                return false;

            // Check if regitration is possible
            if (toVirtualEvent && !evt.HasVirtualEvent)
                return false;
            if (!toVirtualEvent && !evt.HasRealEvent)
                return false;

            // Event is full
            var registrations = GetRegistrationsCount(eventId);
            if (registrations >= evt.MaxAttendees)
                return false;


            var userId = user.LoginName;
            var sec = new SPSecurity.CodeToRunElevated(delegate
            {
                using(var site = new SPSite(SiteId))
                using(var web = site.OpenWeb("/sundmused/"))
                {
                    var attendee = web.SiteUsers[userId];
                    var list = web.Lists["Registrations"];
                    var item = list.Items.Add();
                    item["Title"] = string.Empty;
                    item["Event"] = new SPFieldLookupValue(evt.Id, evt.Title);
                    item["Attendee"] = attendee;
                    item["Author"] = attendee;
                    item["IsOnlineAttendee"] = toVirtualEvent;
                    item.Update();
                }
            });
            SPSecurity.RunWithElevatedPrivileges(sec);
            return true;
        }

        public void RemoveRegistration(SPUser user, int eventId)
        {
            var sec = new SPSecurity.CodeToRunElevated(delegate
            {
                using(var site = new SPSite(SiteId))
                using(var web = site.OpenWeb("/sundmused/"))
                {
                    var item = GetAttendeeItem(web, user.Name, eventId);

                    if (item == null)
                        return;

                    item.Delete();
                }
            });
            SPSecurity.RunWithElevatedPrivileges(sec);
        }

        public int GetRegistrationsCount(int eventId)
        {
            if (eventId <= 0)
                return 0;

            var registrations = 0;
            var sec = new SPSecurity.CodeToRunElevated(delegate
            {
                using(var site = new SPSite(SiteId))
                using (var web = site.OpenWeb("/sundmused/"))
                {
                    var queryString = "<Where>";
                    queryString += "<Eq><FieldRef Name='Event' LookupId='True' /><Value Type='Lookup'>{0}</Value></Eq>";
                    queryString += "</Where>";
                    queryString = string.Format(queryString, eventId);

                    var list = web.Lists["Registrations"];

                    var items = ExecuteQuery(list, queryString);
                    if (items == null)
                        registrations = 0;
                    else 
                        registrations = items.Count;
                }
            });
            SPSecurity.RunWithElevatedPrivileges(sec);

            return registrations;
        }

        public IList<EventDto> GetApprovedEvents()
        {
            IList<EventDto> results = new List<EventDto>();

            var sec = new SPSecurity.CodeToRunElevated(delegate
            {
                using(var site = new SPSite(SiteId))
                using(var web =site.OpenWeb("/sundmused/"))
                {
                    var queryString = "<Where><And><Eq><FieldRef Name='ContentType' /><Value Type='Text'>Event</Value></Eq>";
                    queryString += "<Eq><FieldRef Name='_ModerationStatus' /><Value Type='ModStat'>Kinnitatud</Value></Eq></And></Where>";
                    queryString += "<OrderBy><FieldRef Name='EventStartDate' Ascending='False' /></OrderBy>";

                    results = QueryEvents(web.Lists["Lehed"], queryString);
                }
            });
            SPSecurity.RunWithElevatedPrivileges(sec);

            return results;
        }

        public IList<EventDto> GetUserEvents(int userId)
        {
            IList<EventDto> results = new List<EventDto>();

            var sec = new SPSecurity.CodeToRunElevated(delegate
            {
                using(var site = new SPSite(SiteId))
                using(var web =site.OpenWeb("/sundmused/"))
                {
                    results = GetUserEvents(web, userId);
                }
            });
            SPSecurity.RunWithElevatedPrivileges(sec);

            return results;
        }

        public IList<EventDto> GetAllUserEvents(int userId)
        {
            IList<EventDto> organizerResults = new List<EventDto>();
            IList<EventDto> attendedResults = new List<EventDto>();
            IList<EventDto> results = new List<EventDto>();

            var sec = new SPSecurity.CodeToRunElevated(delegate
            {
                using (var site = new SPSite(SiteId))
                using (var web = site.OpenWeb("/sundmused/"))
                {
                    organizerResults = GetUserEvents(web, userId);
                    attendedResults = GetRegisteredEventsForUser(web, userId);
                }
            });
            SPSecurity.RunWithElevatedPrivileges(sec);

            foreach (var evt in organizerResults)
                if (!results.Contains(evt))
                    results.Add(evt);

            foreach (var evt in attendedResults)
                if (!results.Contains(evt))
                    results.Add(evt);

            results = results.OrderByDescending(d => d.StartTime).ToList();

            return results;
        }

        public IList<EventDto> GetUserEvents(SPWeb web, int userId)
        {
            var queryString = "<Where><And><Eq><FieldRef Name='ContentType' /><Value Type='Text'>Event</Value></Eq>";
            queryString += "<Eq><FieldRef Name='Author' LookupId='True' /><Value Type='User'>{0}</Value></Eq></And></Where>";
            queryString += "<OrderBy><FieldRef Name='EventStartDate' Ascending='False' /></OrderBy>";

            queryString = string.Format(queryString, userId);

            var results = QueryEvents(web.Lists["Lehed"], queryString);
            return results;
        }

        public IList<EventDto> GetRegisteredEventsForUser(int userId)
        {
            IList<EventDto> results = new List<EventDto>();

            var sec = new SPSecurity.CodeToRunElevated(delegate
            {
                using (var site = new SPSite(SiteId))
                using (var web = site.OpenWeb("/sundmused/"))
                {
                    results = GetRegisteredEventsForUser(web, userId);
                }
            });
            SPSecurity.RunWithElevatedPrivileges(sec);

            return results;
        }

        public IList<EventDto> GetRegisteredEventsForUser(SPWeb web, int userId)
        {
            var list = web.Lists["Registrations"];

            var queryString = "<Where><Eq><FieldRef Name='Attendee' LookupId='True' /><Value Type='User'>{0}</Value></Eq></Where>";
            queryString += "<OrderBy><FieldRef Name='Created' Ascending='False' /></OrderBy>";

            queryString = string.Format(queryString, userId);

            var query = new SPQuery{ Query = queryString };
            var items = list.GetItems(query);

            IList<EventDto> results = new List<EventDto>();

            foreach(SPListItem item in items)
            {
                if (item["Event"] == null)
                    continue;

                var eventValue = new SPFieldLookupValue(item["Event"].ToString());
                var evt = GetEventById(eventValue.LookupId);
                results.Add(evt);
            }

            return results;
        }

        public IList<AttendeeDto> GetEventAttendees(int eventId)
        {
            IList<AttendeeDto> result = new List<AttendeeDto>();

            var sec = new SPSecurity.CodeToRunElevated(delegate
            {
                using (var site = new SPSite(SiteId))
                using (var web = site.OpenWeb("/sundmused/"))
                {
                    result = GetEventAttendees(web, eventId);
                }
            });
            SPSecurity.RunWithElevatedPrivileges(sec);

            return result;
        }

        private IList<AttendeeDto> GetEventAttendees(SPWeb web, int eventId)
        {
            var list = web.Lists["Registrations"];
            var queryString = "<Where><Eq><FieldRef Name='Event' LookupId='True' /><Value Type='Lookup'>{0}</Value></Eq></Where>";
            queryString = string.Format(queryString, eventId);

            var query = new SPQuery();
            query.Query = queryString;

            var items = list.GetItems(query);
            var attendees = CreateAttendees(items);

            return attendees;
        }

        public EventDto GetEventById(int eventId)
        {
            EventDto dto = null;

            var sec = new SPSecurity.CodeToRunElevated(delegate
            {
                using(var site = new SPSite(SiteId))
                using(var web =site.OpenWeb("/sundmused/"))
                {
                    dto = GetEventById(web, eventId);
                }
            });
            SPSecurity.RunWithElevatedPrivileges(sec);

            return dto;
        }

        private EventDto GetEventById(SPWeb web, int eventId)
        {
            var queryString = "<Where><And>";
            queryString += "<Eq><FieldRef Name='ID' /><Value Type='integer'>{0}</Value></Eq>";
            queryString += "<Eq><FieldRef Name='ContentType' /><Value Type='Text'>Event</Value></Eq>";
            queryString += "</And></Where>";

            queryString = string.Format(queryString, eventId);

            var items = QueryEvents(web.Lists["Lehed"], queryString);
            if (items == null)
                return null;
            if (items.Count == 0)
                return null;

            return items[0];
        }

        public IList<EventCommentDto> GetEventComments(int eventId)
        {
            IList<EventCommentDto> comments = new List<EventCommentDto>();
            
            var sec = new SPSecurity.CodeToRunElevated(delegate
                {
                    using(var site = new SPSite(SiteId))
                    using(var web = site.OpenWeb("/sundmused/"))
                    {
                        comments = GetEventComments(web, eventId);
                    }
                });
            SPSecurity.RunWithElevatedPrivileges(sec);

            return comments;
            
        }

        private IList<EventCommentDto> GetEventComments(SPWeb web, int eventId)
        {
            var queryString = "<Where><Eq><FieldRef Name='Event' LookupId='True' /><Value Type='Lookup'>{0}</Value></Eq></Where>";
            queryString += "<OrderBy><FieldRef Name='Created' Ascending='True' /></OrderBy>";
            queryString = string.Format(queryString, eventId);

            var list = web.Lists["Comments"];
            var query = new SPQuery {Query = queryString};
            var items = list.GetItems(query);

            var result = CreateEventComments(items);
            return result;
        }

        private IList<EventCommentDto> CreateEventComments(SPListItemCollection items)
        {
            IList<EventCommentDto> events = new List<EventCommentDto>();
            foreach(SPListItem item in items)
            {
                var comment = CreateEventComment(item);
                if (comment != null)
                    events.Add(comment);
            }

            return events;
        }

        private EventCommentDto CreateEventComment(SPListItem item)
        {
            var rep = new UserProfileRepository(item.Web);
            var userValue = new SPFieldUserValue(item.Web, item["Author"].ToString());
            var user = userValue.User;
            var profile = rep.GetUserProfileByUserName(user.LoginName);

            var dto = new EventCommentDto
                       {
                           Title = item.Title,
                           Modified = (DateTime)item["Modified"],
                           Created = (DateTime)item["Created"],
                           Author = profile
                       };
            if (item["Body"] != null)
                dto.Body = item["Body"].ToString();

            return dto;
        }

        private IList<EventDto> QueryEvents(SPList list, string queryString)
        {
            var items = ExecuteQuery(list, queryString);
            return CreateEvents(items);
        }

        private IList<EventDto> CreateEvents(SPListItemCollection items)
        {
            IList<EventDto> events = new List<EventDto>();
            if (items == null)
                return events;

            foreach (SPListItem item in items)
                events.Add(CreateEvent(item));

            return events;
        }

        private IList<AttendeeDto> CreateAttendees(SPListItemCollection items)
        {
            var events = new Dictionary<int, EventDto>(items.Count);
            IList<AttendeeDto> attendees = new List<AttendeeDto>(items.Count);

            foreach(SPListItem item in items)
            {
                if (item["Event"] == null)
                    continue;

                var eventValue = new SPFieldLookupValue(item["Event"].ToString());

                if(!events.ContainsKey(eventValue.LookupId))
                {
                    var itemEvent = GetEventById(eventValue.LookupId);
                    events.Add(eventValue.LookupId, itemEvent);
                }

                var dto = CreateAttendee(item, events[eventValue.LookupId]);
                attendees.Add(dto);
            }

            return attendees;
        }

        private AttendeeDto CreateAttendee(SPListItem item, EventDto eventInstance)
        {
            var dto = new AttendeeDto
                          {
                              Id = item.ID,
                              Title = item.Title,
                              Created = (DateTime) item["Created"],
                              Modified = (DateTime) item["Modified"]
                          };

            if(eventInstance != null)
            {
                dto.Event = eventInstance;
            }
            else if(item["Event"] != null)
            {
                var eventValue = new SPFieldLookupValue(item["Event"].ToString());
                dto.Event = GetEventById(item.Web, eventValue.LookupId);
            }

            if(item["Attendee"] != null)
            {
                var attendeeValue = new SPFieldUserValue(item.Web, item["Attendee"].ToString());
                var attendeeUser = attendeeValue.User;

                if(attendeeUser != null)
                {
                    var profileRepository = new UserProfileRepository(item.Web);
                    var profile = profileRepository.GetUserProfileByUserName(attendeeUser.LoginName);
                    dto.Attendee = profile;
                }
            }

            if (item["IsOnlineAttendee"] != null)
            {
                dto.OnlineAttendee = (bool) item["IsOnlineAttendee"];
            }

            return dto;
        }

        private EventDto CreateEvent(SPListItem item)
        {
            if (item == null)
                return null;

            var dto = new EventDto
                          {
                              Id = item.ID,
                              Title = item.Title,
                              Created = (DateTime) item["Created"],
                              Modified = (DateTime) item["Modified"],
                              FileName = item["FileLeafRef"].ToString(),
                              Comments = GetEventComments(item.ParentList.ParentWeb, item.ID)
                          };
            if (item["ShortDescription"] != null)
                dto.ShortDescription = item["ShortDescription"].ToString();
            if (item["TechnicalLevel"] != null)
                dto.TechnicalLevel = item["TechnicalLevel"].ToString();
            if (item["PlacesCount"] != null)
                dto.MaxAttendees = int.Parse(item["PlacesCount"].ToString());
            if (item["Place"] != null)
                dto.Location = item["Place"].ToString();
            if(item["PlaceOnMap"] != null)
            {
                var link = new SPFieldUrlValue(item["PlaceOnMap"].ToString());
                dto.LocationUrl = link.Url;
            }
            if (item["EventStartDate"] != null)
                dto.StartTime = (DateTime)item["EventStartDate"];
            if (item["EventEndDate"] != null)
                dto.EndTime = (DateTime)item["EventEndDate"];
            if (item["PublishingPageContent"] != null)
                dto.Description = item["PublishingPageContent"].ToString();
            if (item["HasRealEvent"] != null)
                dto.HasRealEvent = bool.Parse(item["HasRealEvent"].ToString());
            if (item["HasVirtualEvent"] != null)
                dto.HasVirtualEvent = bool.Parse(item["HasVirtualEvent"].ToString());
            if (item["TargetAudience"] != null)
            {
                //dto.Audience = item["TargetAudience"].ToString();
                dto.Audiences = new List<string>();
                var choices = new SPFieldMultiChoiceValue(item["TargetAudience"].ToString());
                for (var i = 0; i < choices.Count; i++)
                    dto.Audiences.Add(choices[i]);
            }
            if (item["PercentOfSlides"] != null)
                dto.PercentOfSlides = int.Parse(item["PercentOfSlides"].ToString());
            if (item["PercentOfDemos"] != null)
                dto.PercentOfDemos = int.Parse(item["PercentOfDemos"].ToString());
            if (item["RegistrationEndDate"] != null)
                dto.RegistrationEndDate = (DateTime) item["RegistrationEndDate"];

            return dto;

        }

        public DateTime GetLastModifiedDate()
        {
            using(var site = new SPSite(SiteId))
            using(var web = site.OpenWeb("/sundmused/"))
            {
                var list = web.Lists["Lehed"];

                var queryString = "<Where><And><Eq><FieldRef Name='ContentType' /><Value Type='Text'>Event</Value></Eq>";
                queryString += "<Eq><FieldRef Name='_ModerationStatus' /><Value Type='ModStat'>Kinnitatud</Value></Eq></And></Where>";
                queryString += "<OrderBy><FieldRef Name='Modified' Ascending='False' /></OrderBy>";

                var query = new SPQuery
                                {
                                    Query = queryString,
                                    RowLimit = 1
                                };
                var items = list.GetItems(query);
                if (items.Count == 0)
                    return DateTime.MinValue;

                var eventModified = (DateTime)items[0]["Modified"];

                list = web.Lists["Registrations"];
                queryString = "<OrderBy><FieldRef Name='Modified' Ascending='False' /></OrderBy>";
                query = new SPQuery
                            {
                                Query = queryString,
                                RowLimit = 1
                            };
                items = list.GetItems(query);
                if (items.Count == 0)
                    return eventModified;

                var regModified = (DateTime)items[0]["Modified"];
                if (eventModified > regModified)
                    return eventModified;
                return regModified;
            }
        }
    }
}
